package com.lin.cloud.disk.manage.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.lin.cloud.disk.common.constant.JwtConstant;
import com.lin.cloud.disk.common.constant.RedisConstant;
import com.lin.cloud.disk.common.constant.DepartmentConstant;
import com.lin.cloud.disk.common.enums.ECompanyStatus;
import com.lin.cloud.disk.common.exception.BusinessException;
import com.lin.cloud.disk.common.exception.ParamException;
import com.lin.cloud.disk.entity.dto.CompanyDTO;
import com.lin.cloud.disk.entity.dto.DepartmentDTO;
import com.lin.cloud.disk.entity.dto.PermissionDTO;
import com.lin.cloud.disk.entity.dto.UserDTO;
import com.lin.cloud.disk.entity.pojo.Company;
import com.lin.cloud.disk.entity.pojo.Permission;
import com.lin.cloud.disk.entity.pojo.User;
import com.lin.cloud.disk.entity.pojo.UserDepartment;
import com.lin.cloud.disk.entity.request.JwtTokenRequest;
import com.lin.cloud.disk.entity.request.LoginRequest;
import com.lin.cloud.disk.entity.request.ModifyPasswordRequest;
import com.lin.cloud.disk.entity.request.RegisterRequest;
import com.lin.cloud.disk.entity.response.LoginResponse;
import com.lin.cloud.disk.entity.response.PermissionGroupListResponse;
import com.lin.cloud.disk.entity.response.UserInfoByTokenResponse;
import com.lin.cloud.disk.manage.IUserManageService;
import com.lin.cloud.disk.service.ICompanyService;
import com.lin.cloud.disk.service.IUserDepartmentService;
import com.lin.cloud.disk.service.IUserService;
import com.lin.cloud.disk.shiro.pojo.UserShiroLogin;
import com.lin.cloud.disk.shiro.utils.JwtUtil;
import com.lin.cloud.disk.shiro.utils.SHA256Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 *  用户服务实现类
 * @author ljc
 * @date 2020/12/11 11:45
 */
@Slf4j
@Service
public class UserManageServiceImpl implements IUserManageService {

    @Resource
    private IUserService userService;

    @Resource
    private IUserDepartmentService userDepartmentService;

    @Resource
    private ICompanyService companyService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /** 全字母规则 正整数规则*/
    public static final String STR_ENG_PATTERN="^[a-z0-9]+$";

    @Override
    public LoginResponse login(LoginRequest request) {

        UserShiroLogin userShiroLogin = new UserShiroLogin();
        userShiroLogin.setUserName(request.getUserName());
        userShiroLogin.setPassword(request.getPassword());
        userShiroLogin.setCompanyId(request.getCompanyId());
        Subject subject = SecurityUtils.getSubject();
        try{

            subject.login(userShiroLogin);
        }catch (IncorrectCredentialsException e){
            log.info("密码不正确:" + JSONUtil.toJsonStr(request));
            throw new IncorrectCredentialsException();
        }catch (UnknownAccountException e1){
            log.info("账号不正确:" + JSONUtil.toJsonStr(request));
            throw new UnknownAccountException();
        }

        // 验证通过
        JwtUtil jwtUtil = new JwtUtil();
        String newPassword = SHA256Util.sha256(request.getPassword(),request.getUserName());
        LoginRequest loginRequest = new LoginRequest();
        loginRequest.setUserName(request.getUserName());
        loginRequest.setPassword(newPassword);
        loginRequest.setCompanyId(request.getCompanyId());
        User user = userService.getUserByUserNameAndPassword(loginRequest);
        if (null == user){
            throw new BusinessException("用户不存在");
        }

        // 获取用户所属的公司
        Long companyId = user.getCompanyId();
        Company company = null;
        String companyKey = RedisConstant.AVAILABLE_COMPANY + companyId;
        String companyJson = stringRedisTemplate.opsForValue().get(companyKey);
        if (StringUtils.isNotBlank(companyJson)){
            company = JSONUtil.toBean(companyJson,Company.class);
            //  如果未审核通过 则先审核
            if (null != company && !ECompanyStatus.PASS_REVIEW.getStatus().equals(company.getStatus())){
                throw new BusinessException("公司可能未续费或者在审核中");
            }
        }else{
            company = companyService.getAvailableCompanyById(companyId);
            if (null != company){
                // 计算剩余时间 过期时间 - 当前时间
                Long expireTime = company.getEndTime().getTime() - System.currentTimeMillis();
                stringRedisTemplate.opsForValue().set(RedisConstant.AVAILABLE_COMPANY + companyId, JSONUtil.toJsonStr(company), expireTime, TimeUnit.MILLISECONDS);
            }
        }
        if (null == company){
            throw new BusinessException("公司不存在或公司未续费");
        }


        // 更新最后登录时间
        user.setLastTime(new Date());
        userService.update(user);

        Map<String, Object> map = new HashMap<>(1);
        map.put(JwtConstant.USER_ID, user.getId());
        map.put(JwtConstant.USER_NAME, user.getUsername());
        // accessToken
        String accessToken = jwtUtil.encode(JwtConstant.JWT_ISS, JwtConstant.JWT_ACCESS_TOKEN_EXPIRE_TIME_MILLIS, map);

        // 缓存到Redis中
        stringRedisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
                stringRedisTemplate.opsForValue().set(RedisConstant.JWT_ACCESS_TOKEN + user.getId() ,accessToken , 30, TimeUnit.DAYS);
                stringRedisTemplate.opsForValue().set(RedisConstant.SHIRO_USER + user.getId(),JSONUtil.toJsonStr(user), 30 , TimeUnit.DAYS);
                return null;
            }
        });
        LoginResponse response = new LoginResponse();
        response.setAccessToken(accessToken);
        return response;
    }

    @Override
    public void register(RegisterRequest request) {
        User user = new User();
        BeanUtil.copyProperties(request,user,false);
        Long userId = IdWorker.getId();
        user.setId(userId);
        user.setUsername(request.getUserName());
        user.setCreateBy(request.getUserName());
        user.setCreateTime(new Date());
        // 密码加密
        String newPassword = SHA256Util.sha256(request.getPassword(), request.getUserName());
        user.setPassword(newPassword);
        user.setLastTime(new Date());

        if (userService.isContainUserName(request.getUserName())){
            throw new BusinessException("已存在相同用户名,请选择其他用户名");
        }
        // 加入User表
        userService.insert(user);

//        // 添加用户部门
//        UserDepartment userDepartment = new UserDepartment();
//        Long userDepartmentId = IdWorker.getId();
//        userDepartment.setId(userDepartmentId);
//        userDepartment.setUserId(userId);
//        // 普通用户
//        userDepartment.setDepartmentId(DepartmentConstant.NORMAL_ROLE);
//        userDepartment.setCreateTime(new Date());
//        userDepartment.setCreateBy(request.getUserName());
//        userDepartmentService.insert(userDepartment);
    }


    @Override
    public UserInfoByTokenResponse getUserInfoByToken(JwtTokenRequest request) {
        // 1. 获取userId
        Long userId = JwtUtil.getUserId(request.getAccessToken());
        // 2. 获取用户信息
        String userKey = RedisConstant.SHIRO_USER + userId;
        String userJson = stringRedisTemplate.opsForValue().get(userKey);
        if (null != userJson || !"null".equals(userJson)){
            UserDTO userDTO = JSONUtil.toBean(userJson, UserDTO.class);
            if (null != userDTO){
                Subject subject = SecurityUtils.getSubject();
                subject.isPermitted("手动触发授权");
                // 3. 获取权限
                String departmentKey = RedisConstant.SHIRO_ROLES + userId;
                String cacheKey = RedisConstant.USER_PERMISSION_LIST + userId;
                String companyKey = RedisConstant.AVAILABLE_COMPANY + userDTO.getCompanyId();
                List<DepartmentDTO> departmentDTOList = new ArrayList<>();
                List<PermissionDTO> permissionDTOList = new ArrayList<>();
                // 部门列表
                Set<String> departments = stringRedisTemplate.opsForSet().members(departmentKey);
                departments.stream().forEach(department->{departmentDTOList.add(JSONUtil.toBean(department,DepartmentDTO.class));});
                List<String> permissionJsonList = stringRedisTemplate.opsForList().range(cacheKey, 0 , -1);
                // 权限列表
                permissionJsonList.stream().forEach(permission->{permissionDTOList.add(JSONUtil.toBean(permission,PermissionDTO.class));});
                // 公司主体信息
                CompanyDTO companyDTO = JSONUtil.toBean(stringRedisTemplate.opsForValue().get(companyKey),CompanyDTO.class);
                UserInfoByTokenResponse response = new UserInfoByTokenResponse();
                response.setUserId(userId);
                response.setUserDTO(userDTO);
                response.setDepartmentDTOList(departmentDTOList);
                List<Permission> permissionList = new ArrayList<>(permissionDTOList.size());
                Permission permission = null;
                for (PermissionDTO permissionDTO: permissionDTOList) {
                    permission = new Permission();
                    BeanUtil.copyProperties(permissionDTO,permission,false);
                    permissionList.add(permission);
                }
                response.setPermissionsList(new PermissionGroupListResponse(permissionList));
                response.setCompanyDTO(companyDTO);
                return response;
            }
        }
        return null;
    }

    @Override
    public void modifyPassword(ModifyPasswordRequest request) {

        Long userId = JwtUtil.getUserId(request.getAccessToken());
        if (null != userId || 0 != userId){
            // 旧密码加密
            String encryptedPassword = SHA256Util.sha256(request.getOldPassword(),request.getUserName());

            LoginRequest loginRequest = new LoginRequest();
            loginRequest.setUserName(request.getUserName());
            loginRequest.setPassword(encryptedPassword);
            User user = userService.getUserByUserNameAndPassword(loginRequest);
            if (null != user){
                // 新密码加密
                encryptedPassword = SHA256Util.sha256(request.getNewPassword(),request.getUserName());
                user.setPassword(encryptedPassword);
                userService.update(user);

                // 调用删除缓存
                JwtTokenRequest jwtTokenRequest = new JwtTokenRequest();
                jwtTokenRequest.setAccessToken(request.getAccessToken());
                this.batchDeleteCacheByToken(jwtTokenRequest);
            }else {
                throw new BusinessException("用户不存在 " + JSONUtil.toJsonStr(request));
            }
        }else{
            throw new ParamException("用户ID不存在 " + JSONUtil.toJsonStr(request));
        }

    }

    @Override
    public void batchDeleteCacheByToken(JwtTokenRequest request) {
        // 1. 获取userId
        Long userId = JwtUtil.getUserId(request.getAccessToken());
        // 2. 删除缓存
        List<String> deleteKeyList = new ArrayList<>();
        deleteKeyList.add(RedisConstant.JWT_ACCESS_TOKEN + userId);
        deleteKeyList.add(RedisConstant.SHIRO_USER + userId);
        deleteKeyList.add(RedisConstant.SHIRO_ROLES + userId);
        deleteKeyList.add(RedisConstant.SHIRO_PERMISSIONS + userId);
        deleteKeyList.add(RedisConstant.USER_PERMISSION_LIST + userId);
        stringRedisTemplate.delete(deleteKeyList);
    }

}
